package org.trc.traffic.analysis;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Vector;

/**
 * 采用ST-DBSCAN 聚集 congestion cluster
 * 
 * @author Administrator
 *
 */

public class AggLinkCongestionClustering { 
	
	double timeCost = 0;

	//输入参数
	protected float minSpatialThreshold;

	protected float minTimeThreshold;
	
	protected int minNum;
	
	//聚簇结果
	protected Map<String, AggLinkCongestionCluster> congestionClusterSet;

	//聚簇
	public void buildAggCongestionCluster(String linkID, ArrayList <LinkCongestionCluster> allLinkClusterList, float minSpatialThreshold, float minTimeThreshold, int minNum) {

		timeCost = System.currentTimeMillis();
		
        // 数据集初始化
		this.minSpatialThreshold = minSpatialThreshold;

		this.minTimeThreshold = minTimeThreshold;
		
		this.minNum = minNum;

		// LinkCongestionCluster分组

		int clusterNum = 1;
		LinkCongestionCluster linkCC = null;

		for (int i = 0; i < allLinkClusterList.size(); i++) {
			linkCC = allLinkClusterList.get(i);
			if (!linkCC.clusterID.startsWith("AggLink")) {// 未分组
				if(this.expandAggCongestionCluster(linkCC, allLinkClusterList,linkID, clusterNum))
					clusterNum++;
			}
		}

		// 生成 AggLinkCongestionCluster
		this.congestionClusterSet = Collections.synchronizedMap(new HashMap<String, AggLinkCongestionCluster>());
		
		AggLinkCongestionCluster congestionCluster = null;

		for (LinkCongestionCluster lCC : allLinkClusterList) {
			String clusterID = lCC.clusterID;
			if (!linkCC.clusterID.startsWith("AggLink")) 
				continue;
			if (!congestionClusterSet.containsKey(clusterID)) {
				congestionCluster = new AggLinkCongestionCluster(clusterID);
				congestionCluster.add(lCC);
				congestionClusterSet.put(clusterID, congestionCluster);
			}else
				congestionCluster = congestionClusterSet.get(clusterID);

			congestionCluster.add(lCC);
		}
		
		timeCost = System.currentTimeMillis() - this.timeCost;

	}

	private boolean expandAggCongestionCluster(LinkCongestionCluster coreLinkCluster, ArrayList<LinkCongestionCluster> allLinkClusterList,
			String linkID, int clusterNum) {	

/*		if(coreLinkCluster.clusterID.equals("link_45546302894_1_2009-03-10"))
			System.out.println("Note");*/
			
		List<LinkCongestionCluster> resultDataSet = this.STRangeQuery(coreLinkCluster, allLinkClusterList);

		if (resultDataSet.size() <  minNum)
			return false;

		coreLinkCluster.clusterID = "AggLink_" + linkID + "_" + Integer.toString(clusterNum);
		
		for (LinkCongestionCluster mlCC : resultDataSet) {// 标记簇ID
			if (mlCC.clusterID.startsWith("link")){
				mlCC.clusterID = "AggLink_" + linkID + "_" + Integer.toString(clusterNum);
				expandAggCongestionCluster(mlCC, allLinkClusterList, linkID,clusterNum);			
			}
		}
		
		return true;
	}

	//时空邻域
	private List<LinkCongestionCluster> STRangeQuery(LinkCongestionCluster coreLinkCluster, ArrayList<LinkCongestionCluster> allLinkClusterList) {
		Vector<LinkCongestionCluster> stRange_List = new Vector<LinkCongestionCluster>();

		for (LinkCongestionCluster lCC : allLinkClusterList) {
			if (lCC.equals(coreLinkCluster))
				continue;
/*			if(lCC.clusterID.equals("link_45546302894_1_2009-03-09"))
			    System.out.println("Note");*/			
			if (timeWithin(lCC, coreLinkCluster))
				stRange_List.add(lCC);
		}

		return stRange_List;
	}

	//时间相似Jarcard计算
	private boolean timeWithin(LinkCongestionCluster lCC1, LinkCongestionCluster lCC2) {
			
		float union =  Math.max(lCC1.endTime, lCC2.endTime) - Math.min(lCC1.startTime, lCC2.startTime) + 1;
		
		float intersection =  Math.min(lCC1.endTime, lCC2.endTime) - Math.max(lCC1.startTime, lCC2.startTime) + 1;
		
		if(intersection <= 0)
			return false;
		
		float timeDistance1 = intersection/union;
		
		if(timeDistance1 > this.minTimeThreshold){
			//System.out.println("linkClusterID:" + lCC1.clusterID + " U:" + union + " I:" + intersection);
			return true;
		}
		else
			return false;	
	}

	public ArrayList <AggCongestionCluster> getCongestionCluster() {
		return new ArrayList <AggCongestionCluster> (congestionClusterSet.values());
	}
	
	public static void main(String[] args) throws ParseException{
		SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-mm-dd");
		
		ArrayList <LinkCongestionCluster> allLinkClusterList = new ArrayList <LinkCongestionCluster> ();
		
		STLinkCongestionClustering stLinkCongestionClustering = new STLinkCongestionClustering();
		
		stLinkCongestionClustering.buildCongestionCluster("45546302894", 8, 10, dateFormat.parse("2009-3-8"), 0, 1);
			
		allLinkClusterList.addAll((Collection<? extends LinkCongestionCluster>) stLinkCongestionClustering.getCongestionCluster());
						
		stLinkCongestionClustering.buildCongestionCluster("45546302894", 8, 10, dateFormat.parse("2009-3-9"), 0, 1);
		
		allLinkClusterList.addAll((Collection<? extends LinkCongestionCluster>) stLinkCongestionClustering.getCongestionCluster());
		
		stLinkCongestionClustering.buildCongestionCluster("45546302894", 8, 10, dateFormat.parse("2009-3-10"), 0, 1);
		
		allLinkClusterList.addAll((Collection<? extends LinkCongestionCluster>) stLinkCongestionClustering.getCongestionCluster());
		
		AggLinkCongestionClustering aggLinkCongestionClustering = new AggLinkCongestionClustering();
		
		aggLinkCongestionClustering.buildAggCongestionCluster("", allLinkClusterList, 0f, 0.4f, 2);
		
		ArrayList<AggCongestionCluster> congestionClusters = aggLinkCongestionClustering.getCongestionCluster();
		
		for (AggCongestionCluster congestionCluster : congestionClusters) {
			AggLinkCongestionCluster cc = (AggLinkCongestionCluster)congestionCluster;
			System.out.print("CluterID: " + cc.clusterID + ",");
			System.out.print(" StartTime: " + (cc.startTime -96) + ",");
			System.out.print(" EndtTime: " + (cc.endTime - 96));
			System.out.print(" Dur: " + cc.dur);
			System.out.println();
		}
	
	}

}
